アプリケーションは、ハードウェア抽象化層(HAL)の AudioOutputUnit を使って、単一のオーディオデバイスとインタフェースをとることができます。AudioOutputUnit (AUHAL) ユニットは、<CoreAudio/AudioHardware.h> で定義されている AUHAL を使ってオーディオデバイスから入力を取り込むには、以下の手順に従ってください。 1. AUHAL を開きます。 2. AUHAL の入力を使用可能にします。 3. デフォルトの入力デバイスを AUHAL の現在の入力デバイスに設定します。 4. デバイスフォーマットを取得し、希望のオーディオフォーマットを指定します。 5. 入力コールバックを作成し、AUHAL に登録します。 6. 必要なバッファを割り当てます。 7. AUHAL を初期化して開始します。 これらの手順について、このテクニカルノートで詳しく説明します。 AudioOutputUnit の作成まず、他の Audio Unit を取得する場合と同様に、Component Description を使って AudioOutputUnit を取得する必要があります。 リスト 1. AudioOutputUnit を開く方法 Component comp; ComponentDescription desc; // 数種類の Audio Unit がある。 // いくつかのオーディオユニットは入力、ミキサー、DSP ユニットとして機能する。 // 一覧については、AUComponent.h を参照。 desc.componentType = kAudioUnitType_Output; // すべてのコンポーネントは subType を持っており、これがコンポーネントの機能を // 明確に記述する。 desc.componentSubType = kAudioUnitSubType_HALOutput; // AUComponent.h のすべての Audio Unit は、 //"kAudioUnitManufacturer_Apple" を Manufacturer として使用する必要がある。 desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; // desc のスペックに合ったコンポーネントを検出する。 comp = FindNextComponent(NULL, &desc); if (comp == NULL) exit (-1); // コンポーネントの提供するサービスにアクセスする。 OpenAComponent(comp, &InputUnit); Audio Unit 接続の概要Audio Unit は、異なる 2 つの Audio Unit 間の直接接続の概念を具現化しています。Audio Unit は、データを生成するように要求されると、コールバック関数または接続されている別の Audio Unit からデータを受け取ることができます。接続の例として、A1 と A2 という 2 つの Audio Unit があれば、A1 を A2 に接続します (A1-->A2)。A2 がデータを生成するように要求されると、データストリームは基本的に A1 から A2 へ「プル」されて処理されます。そのため、Audio Unit 間の接続は、同じオーディオストリームフォーマットを共有している必要があります。Audio Unit と接続の詳細については、「Audio and MIDI on Mac OS X」 を参照してください。
図 1 は、オーディオデバイスとアプリケーションの間のオーディオデータの流れを示しています。アプリケーションは、処理を単純化するために、Audio Unit を AUHAL のいずれかの要素(バス)に接続することができます。つまり、デバイスにオーディオを出力するソースとして Audio Unit を使う場合は、次の接続を使用します。 表 1. デバイスへのオーディオの出力
オーディオデバイスの入力データを取得したい場合は、次のように接続します。 表 2. デバイスへの音声の取り込み
もちろん、入出力を提供する内蔵オーディオデバイスのようなデバイスについては、次の接続を作成することで、ソフトウェアプレイスルーのメカニズムを確立することができます。 表 3. 簡単なソフトウェアプレイスルー
入出力の間に 1 つ以上のオーディオユニットを挿入することにより、入力から出力まで、オーディオへの必要な処理操作を何度でも実行できます。ここで、マルチバンドのコンプレッサ Audio Unit を使って内蔵オーディオ入力を処理する例を挙げてみましょう。これを行うには、次の接続を作成します。 表 4. マルチバンドのコンプレッサ Audio Unit による内蔵オーディオ入力の処理
(AudioToolbox.framework の)AUGraph API は、これらの接続を管理できます。 2 つの別々のオーディオデバイスを使う場合は、2 つの AUHAL が必要となります。しかし、それぞれの AUHAL は個別の I/O プロセスで実行するため、2 つの AUHAL の間に直接の接続を確立できません。通知メカニズムを使用して、出力デバイスに対してデータが到着したことを通知し、データを渡す必要があります。 注意: AUGraph ごとに使用できる AudioOutputUnit は 1 つのみです。 IO の有効化AUHAL オブジェクトを作成したら、デバイス入力を取得するために、Audio Unit の入力範囲にある IO を使用可能にする必要があります。入力は、AUHAL の要素 1 にある リスト 2. AudioOutputUnit の入力の有効化と出力の無効化 UInt32 enableIO; UInt32 size=0; // AudioUnitSetProperty を使用する場合は、メソッドの 4 番目のパラメータが //AudioUnitElement を参照する。AudioOutputUnit を使用する場合は、 // 入力要素が '1' になり、出力要素が '0' になる。 enableIO = 1; AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, // 入力要素 &enableIO, sizeof(enableIO)); enableIO = 0; AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, // 出力要素 &enableIO, sizeof(enableIO)); AudioOutputUnit の現在のデバイスの設定AUHAL には、インタフェースをとるデバイスがなければなりません。この例では、現在のデバイスとしてシステムのデフォルト入力デバイスを選択します。 リスト 3. AudioOutputUnit の現在のデバイスをデフォルト入力デバイスに設定する方法 OSStatus SetDefaultInputDeviceAsCurrent(){ UInt32 size; OSStatus err =noErr; size = sizeof(AudioDeviceID); AudioDeviceID inputDevice; err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, &inputDevice); if (err) return err; err =AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(inputDevice)); return err; } オーディオデータのフォーマットについてAUHAL は、入出力ともに、デバイスのオーディオデータストリームを単一のインターリーブされていないストリームにフラット化します。AUHAL には、この変換を実行する オーディオデバイスにデータを出力するには、フォーマットを必ず AUHAL の要素 0 の出力範囲で明示します。オーディオデバイスフォーマットを取得するには、 デバイスから入力を取得するには、デバイスフォーマットを必ず AUHAL の要素 1 の入力範囲で明示します。このため、希望のフォーマットは AUHAL の 要素 1 の出力範囲に設定する必要があります。内部の リスト 4. 希望する「入力」フォーマットの設定 CAStreamBasicDescription DeviceFormat; CAStreamBasicDescription DesiredFormat; //CAStreamBasicDescriptions を「裸」の //AudioStreamBasicDescriptions の代わりに使うことでエラーを最小限にします。 //CAStreamBasicDescription.h は、CoreAudio SDK に含まれています。 UInt32 size = sizeof(CAStreamBasicDescription); // 入力デバイスフォーマットを取得する AudioUnitGetProperty (InputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &DeviceFormat, &size); // 希望のフォーマットをデバイスのサンプルレートに設定する DesiredFormat.mSampleRate = DeviceFormat.mSampleRate; // フォーマットを出力範囲に設定する AudioUnitSetProperty( InputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &DesiredFormat, sizeof(CAStreamBasicDescription); チャネルマッピングオーディオデバイスのチャネルと希望フォーマットのチャネルのデータフォーマットが 1:1 の比率に対応していない場合は、チャネルマッピングが必要です。チャンルマッピングでは、Audio Unit がどんなデバイスチャネルと対話するかを指定します。これを設定する必要があるのは、デフォルトのマッピング以外の設定を使う予定がある場合に限られます。 表 5. デフォルトのチャネルマップ(チャネルマッピングは不要)
たとえば、入力に使用する 4 チャネルのデバイスがあり、ステレオ入力としてデバイスの 2 または 3 チャネルのみが必要であったとします。必要なチャネルを AUHAL のチャネルに割り当てる(マップする)必要があります。AUHAL のチャネルマップを作成するには、マップの「出力先」ごとに SInt32 の配列を作成する必要があります。Sint32 の配列の各要素は、出力先に送られるソースチャネルのインデックス、または "ソースなし" を意味する -1 を参照します。この例では、2 つの要素の配列を作成し、その値を -1 に初期設定します。マップしたいチャネルには、チャネルマップ配列の要素の値を 2 と 3 に設定します。その結果、チャネルマップは [2,3] になります。 表 6. 4 -> 2 チャネルマップ
リスト 5. 入力用の 4 -> 2 チャネルマッピングの例 SInt32 *channelMap =NULL; UInt32 numOfChannels = DesiredFormat.mChannelsPerFrame; //2 チャネル UInt32 mapSize = numOfChannels *sizeof(SInt32); channelMap = (SInt32 *)malloc(size); // 必要な入力の各チャネルに、デバイスの出力チャネルから // チャネルをマップする。 for(UInt32 i=0;i<numOfChannels;i++) { channelMap[i]=-1; } //channelMap[desiredInputChannel] = deviceOutputChannel; channelMap[0] = 2; channelMap[1] = 3; AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 1, channelMap, size); free(channelMap); AudioOutputUnit の入力プロシージャの作成次に、AUHAL の入力プロシージャを登録する必要があります。AUHAL が入力デバイスから新しいデータを受け取ると、このプロシージャが呼び出されます。 リスト 6. AudioOutputUnit の入力プロシージャの作成 void MyInputCallbackSetup() { AURenderCallbackStruct input; input.inputProc = InputProc; input.inputProcRefCon = 0; AudioUnitSetProperty( InputUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); } AudioOutputUnit の初期化と開始AUHAL がデバイスから入力を受け取るように設定します。データの取得を開始するには、Audio Unit を初期化して開始する必要があります。 リスト 7. AUHAL の開始 OSStatus InitAndStartAUHAL() { OSStatus err =noErr; err = AudioUnitInitialize(InputUnit); if(err) return err; err = AudioOutputUnitStart(InputUnit); return err; } AudioOutputUnit からのデータの取得AUHAL は、オーディオデバイスとの間でオーディオデータを送受信できる Audio Unit です。AUHAL からオーディオを受信するには、Audio Unit の出力範囲から取得する必要があります。実際には、これはクライアントが 下の例では、入力プロシージャ内から リスト 8. AudioUnitRender を使ったデータの取得 AudioBufferList * theBufferList; /* バッファデータを保持するために割り当て */ OSStatus InputProc( void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { OSStatus err =noErr; err= AudioUnitRender(InputUnit, ioActionFlags, inTimeStamp, inBusNumber, // 入力データの '1' になる inNumberFrames, // 必要なフレーム数 theBufferList); return err; } まとめAUHAL を使ってオーディオデバイスとインタフェースをとると、アプリケーションとオーディオデバイス間の対話を大幅に簡素化できます。この Audio Unit は、オーディオデバイス情報の取得と、オーディオデータの転送や取得を行う際にオーディオデベロッパを支援します。 サンプルコード、参考資料、注意事項サンプルコード - ComplexPlayThruサンプルコード - SimplePlayThru Core Audio Preliminary doc Audio and MIDI on Mac OS X -May 2001 注意: システムの条件QuickTime v6.5 をインストールした. Mac OS X v10.2 および v10.3。 ドキュメントの改訂履歴
掲載日: 2004-08-23 |